- Often, we need to render something and then manipulate results
- This means we need to be able to render to a texture
- Then we can use the result like any ordinary texture
- GL has entity called Framebuffer Object
- This lets us render to a texture instead of the screen
- Then we can use the texture like any other image
- Do-nothing: Just renders to fbo, then to screen
- Draw code:
void draw(){
globs->prog.use();
globs->fbo.setAsRenderTarget(true);
globs->camera.setUniforms();
globs->lightManager.setUniforms();
Program::setUniform("worldMatrix", mat4::identity() );
globs->dungeon.draw();
globs->fbo.unsetAsRenderTarget();
globs->fboprog.use();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
globs->fbo.texture->bind(0);
globs->fsq.draw();
}
layout(location=0) in vec3 position;
layout(location=1) in vec2 texcoord;
out vec2 v_texCoord;
void main(){
gl_Position = vec4( position.xy, -1.0, 1.0 );
v_texCoord = texcoord;
}
in vec2 v_texCoord;
out vec4 color;
layout(binding=0) uniform sampler2DArray tex;
void main(){
color = texture( tex, vec3(v_texCoord,0.0) );
}
- Color manipulation: Alter hue/saturation/value
- Explain: Color space: HSV
- RGB to HSV: From N. Schaller: Given: r,g,b in 0…1 range:
vec3 rgb2hsv(vec3 color)
{
float r=color.r;
float g=color.g;
float b=color.b;
float mx = max(r,max(g,b));
float mn = min(r,min(g,b));
float h,s,v;
v = mx; //value
if(mx != 0.0 ){
s = (mx-mn)/mx; //saturation
if(r == mx)
h = (g-b)/(mx-mn);
else if(g == mx)
h = 2.0+(b-r)/(mx-mn);
else
h = 4.0+(r-g)/(mx-mn);
h *= 60.0;
if( h < 0.0 )
h += 360.0;
} else {
h=s=0.0;
}
return vec3(h,s,v);
}
- HSV to RGB: Also from N. Schaller: Given h in 0…360 range, s,v in 0…1 range:
vec3 hsv2rgb(vec3 hsv)
{
float h = hsv[0];
float s = hsv[1];
float v = hsv[2];
float r,g,b;
if(s == 0.0)
r=g=b=v;
else{
h=h/60.0;
float ipart = floor(h);
float fpart = h-ipart;
float A=v*(1.0-s);
float B=v*(1.0-s*fpart);
float C=v*(1.0-s*(1.0-fpart));
if(ipart == 0.0){
r=v; g=C; b=A;
} else if( ipart==1.0){
r=B; g=v; b=A;
} else if( ipart==2.0 ){
r=A; g=v; b=C;
} else if( ipart==3.0){
r=A; g=B; b=v;
} else if( ipart==4.0 ){
r=C; g=A; b=v;
} else {
r=v; g=A; b=B;
}
}
return vec3(r,g,b);
}
- Example: Lower or raise saturation
- Only change: FBO FS
void main(){
color = texture( tex, vec3(v_texCoord,0.0) );
vec3 tmp = rgb2hsv(color.rgb);
tmp[1] += deltaSaturation;
tmp[1] = clamp(tmp[1],0.0,1.0);
color.rgb = hsv2rgb(tmp);
}
- Old TV with prominent scanlines:
vec4 online = texelFetch( tex, ivec3( gl_FragCoord.xy, 0 ), 0 );
vec4 aboveline = texelFetch( tex, ivec3( gl_FragCoord.x, gl_FragCoord.y-1.0, 0 ), 0 );
vec4 belowline = texelFetch( tex, ivec3( gl_FragCoord.x, gl_FragCoord.y+1.0, 0 ), 0 );
int y = int(gl_FragCoord.y);
switch( y%4 ){
case 0: color = online; break;
case 1: color = 0.5 * aboveline; break;
case 2: color = vec4(0.0, 0.0, 0.0, 1.0); break;
case 3: color = 0.5 * belowline; break;
}
color.a = 1.0;
- Example: Posterize scene
- Posterize = quantize color resolution
- FS
in vec2 v_texCoord;
out vec4 color;
layout(binding=0) uniform sampler2DArray tex;
void main(){
vec4 tmp = texture( tex, vec3(v_texCoord,0.0) );
tmp = floor( tmp * 8.0 ) / 8.0;
color = vec4(tmp.xyz,1.0);
}
in vec2 v_texCoord;
out vec4 color;
layout(binding=0) uniform sampler2DArray tex;
void main(){
//assumes FBO size matches screen size
ivec2 coord = ivec2(gl_FragCoord.xy);
coord = coord & ivec2(~15);
vec4 texcolor = texelFetch( tex, ivec3(coord,0), 0 );
color = texcolor;
}
- Another example… Not sure where this is useful…
- smoothstep( a, b, c )
- If c < a → 0
- If c > b → 1
- Else, smooth ramp between a and b
in vec2 v_texCoord;
out vec4 color;
layout(binding=0) uniform sampler2DArray tex;
void main(){
float incr = 1.0/32.0;
vec2 tmp = v_texCoord / vec2(incr);
tmp = floor(tmp);
tmp *= incr;
tmp += incr * 0.5;
float dist = distance(tmp,v_texCoord);
vec4 texcolor = texture(tex, vec3(tmp,0));
float ramp = 1.0 - smoothstep( incr*0.2, incr*0.5, dist );
color.rgb = ramp * texcolor.rgb;
color.a = texcolor.a;
}
- Cross-fade between two scenes
- We have two fbo’s
- We have two cameras: cam1 and cam2
- When not fading: Use cam1 as normal
- To begin fade:
- Set cam2 to the new camera position
- Set float fadePercent = 0
- Set boolean isFading = true
- On update(): If isFading:
- Increment fadePercent
- If fadePercent ≥ 1:
- Set isFading=false
- Set cam1 = cam2
void draw(){
globs->prog.use();
if( isFading ){
globs->fbo.setAsRenderTarget(false);
globs->cam1.setUniforms();
drawObjects();
globs->fbo2.setAsRenderTarget(true);
globs->cam2.setUniforms();
drawObjects();
globs->fbo2.unsetAsRenderTarget();
globs->fboprog.use();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
globs->fbo.texture->bind(0);
globs->fbo2.texture->bind(1);
Program::setUniform("fadePercent",fadePercent);
globs->fullscreenquad.draw();
} else {
globs->camera.setUniforms();
drawObjects();
}
}
in vec2 v_texCoord;
out vec4 color;
layout(binding=0) uniform sampler2DArray tex1;
layout(binding=1) uniform sampler2DArray tex2;
void main(){
vec4 c1 = texture( tex1, vec3(v_texCoord,0.0) );
vec4 c2 = texture( tex2, vec3(v_texCoord,0.0) );
color = mix( c1, c2, fadePercent );
}
- Cutscene: Scene shrinks to a single vertical line in center, then expands back to normal size
- Globals: isFlipping (bool), flipPercent (float)
- In update(): if isFlipping, increment flipPercent
void draw(){
globs->prog.use();
if( isFlipping ){
globs->fbo.setAsRenderTarget(false);
drawObjects();
globs->fbo.unsetAsRenderTarget();
globs->fboprog.use();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
globs->fbo.texture->bind(0);
if( flipPercent < 0.5 )
Program::setUniform("worldMatrix", scaling( 1.0-flipPercent*2.0,1,1) );
else
Program::setUniform("worldMatrix", scaling( (flipPercent-0.5)*2.0,1,1) );
globs->fullscreenquad.draw();
} else {
drawObjects();
}
}
- VS is a bit different: We need to use worldMatrix
out vec2 v_texCoord;
void main(){
vec4 p = vec4(position.xy,0,1);
p = p * worldMatrix;
gl_Position = vec4( p.xy, -1.0, 1.0 );
v_texCoord = texcoord;
}
- FS is just like do-nothing example given previously
- 1960’s Batman (TM) style cutscene: Screen spins and shrinks, then spins and expands
- Same idea as before: World matrix just has rotate & scale components
- Known as Sobel, Prewitt, or Laplace edge detection
- Use a convolution kernel
-
- Example: -1 0 1 / -2 0 2 / -1 0 1
- Example: 1 2 1 / 0 0 0 / -1 -2 -1
- Example: 0 1 0 / 1 -4 1 / 0 1 0